﻿#pragma once

#include <entt.hpp>

#include "SourceCode/Scene/Scene.h"
#include "SourceCode/Scene/Components.h"

namespace Hazal
{
	class CEntity
	{
	public:
		CEntity() = default;

		CEntity(entt::entity handle, CScene* pScene);

		CEntity(CEntity& rOther) = default;

		~CEntity();

	public:
		operator entt::entity() { return m_entityHandle; }

		operator bool() const { return (int32_t)m_entityHandle >= 0 && nullptr != m_pScene; }

		operator uint32_t() const { return (uint32_t)m_entityHandle; }

		operator entt::entity() const { return m_entityHandle; }

		bool operator==(const CEntity& entity) const { return m_entityHandle == entity.m_entityHandle; }

		bool operator!=(const CEntity& entity) const { return !(*this == entity); }

		void operator=(const CEntity& entity) { m_entityHandle = entity.m_entityHandle; m_pScene = entity.m_pScene; }

		CUUID GetUUID() { return GetComponent<SIDComponent>().m_id; }

		std::string GetTag() { return GetComponent<STagComponent>().m_strTag; }

	public:
		template<typename T, typename ...Args>
		T& AddComponent(Args&& ... args)
		{
			HZ_CORE_ASSERT(!HasComponent<T>(), "Current entity already has the component!");

			auto& component = m_pScene->m_registry.emplace<T>(m_entityHandle, std::forward<Args>(args)...);
			m_pScene->OnComponentAdded<T>(*this, component);

			return component;
		}

		template<typename T, typename ...Args>
		T& AddOrReplaceComponent(Args&& ... args)
		{
			auto& component = m_pScene->m_registry.emplace_or_replace<T>(m_entityHandle, std::forward<Args>(args)...);
			m_pScene->OnComponentAdded<T>(*this, component);

			return component;
		}

		template<typename T>
		T& GetComponent()
		{
			HZ_CORE_ASSERT(HasComponent<T>(), "Current entity does not has the component!");

			return m_pScene->m_registry.get<T>(m_entityHandle);
		}

		template<typename T>
		bool HasComponent()
		{
			HZ_CORE_ASSERT(nullptr != m_pScene, "The scene ptr is null!");

			return m_pScene->m_registry.any_of<T>(m_entityHandle);
		}

		template<typename T> 
		void RemoveComponent()
		{
			HZ_CORE_ASSERT(HasComponent<T>(), "Current entity does not has the component!");
		
			m_pScene->m_registry.remove<T>(m_entityHandle);
		}

	private:
		entt::entity m_entityHandle{ entt::null };
		CScene* m_pScene = nullptr;
	};
}
